package pl.edu.mimuw.irs.rcp.controller.editor.roles;

import org.eclipse.gef.EditDomain;
import org.eclipse.gef.commands.CommandStack;
import org.eclipse.jface.viewers.TableViewer;
import org.eclipse.swt.SWT;
import org.eclipse.swt.dnd.DND;
import org.eclipse.swt.dnd.DragSource;
import org.eclipse.swt.dnd.DragSourceEvent;
import org.eclipse.swt.dnd.DragSourceListener;
import org.eclipse.swt.dnd.DropTarget;
import org.eclipse.swt.dnd.DropTargetAdapter;
import org.eclipse.swt.dnd.DropTargetEvent;
import org.eclipse.swt.dnd.TextTransfer;
import org.eclipse.swt.dnd.Transfer;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Table;
import org.eclipse.swt.widgets.TableItem;

import pl.edu.mimuw.irs.core.model.design.resource.Role;
import pl.edu.mimuw.irs.core.model.design.resource.RoleInstance;
import pl.edu.mimuw.irs.core.model.design.resource.Roles;
import pl.edu.mimuw.irs.rcp.controller.editor.util.command.AddMemberCommand;
import pl.edu.mimuw.irs.rcp.util.Icon;
import pl.edu.mimuw.irs.rcp.util.MessageCode;
import pl.edu.mimuw.irs.rcp.util.UITexts;
import pl.edu.mimuw.irs.rcp.util.dnd.RolesInstancesTransfer;
import pl.edu.mimuw.irs.rcp.util.dnd.RolesTransfer;

public class RolesTableWrapper {

	// *** Attributes

	private Roles roles;
	private EditDomain editDomain;
	private TableViewer tableViewer;
	public void setRoles(Roles roles) {
		this.roles = roles;
		redrawTable();
	}
	public Roles getRoles() {
		return roles;
	}
	public EditDomain getEditDomain() {
		return editDomain;
	}
	public TableViewer getTableViewer() {
		return tableViewer;
	}
	public CommandStack getCommandStack() {
		return getEditDomain().getCommandStack();
	}
	
	// *** Constructors

	public RolesTableWrapper(Composite parent, int style, 
			Roles roles, EditDomain editDomain) {
		tableViewer = new TableViewer(parent, style);
		this.roles = roles;
		this.editDomain = editDomain;
		
		redrawTable();
		
		// Drag'N'Drop
		addDragSource();
		if (getEditDomain() != null) addDropTarget();
	}
	
	// *** Methods
	
	public void redrawTable() {
		tableViewer.getTable().removeAll();
		if (getRoles() == null) return;
		for (Role role : getRoles().values()) {
			TableItem item = new TableItem(tableViewer.getTable(), SWT.BORDER);
			item.setText(new String[] {role.getName()});
			item.setImage(Icon.BLUE_SKILL.getImage());
			// Store resource in table item
			item.setData(role);
		}
		
		tableViewer.getTable().redraw();
	}
	
	private void addDragSource() {
		final Table table = tableViewer.getTable();
		
		int operations = DND.DROP_LINK | DND.DROP_COPY;
		DragSource source = new DragSource(table, operations);
		
		Transfer[] types = new Transfer[] { RolesTransfer.getInstance(), 
				TextTransfer.getInstance() };
	 	source.setTransfer(types);
		
	 	source.addDragListener(new DragSourceListener() {
	 		public void dragStart(DragSourceEvent event) {
	 			// Only start the drag if there is actually text in the
	 			// label - this text will be what is dropped on the target.
	 			if (table.getSelection().length == 0) {
	 				event.doit = false;
	 			}
	 		}
	 		public void dragSetData(DragSourceEvent event) {
	 			// Provide the data of the requested type.
	 			if (RolesTransfer.getInstance().isSupportedType(event.dataType)) {
	 				Role[] roles = new Role[table.getSelection().length];
	 				Object[] selection = table.getSelection();
	 				for (int i = 0; i < selection.length; i++) {
	 					TableItem item = (TableItem)selection[i];
	 					roles[i] = (Role)item.getData();
	 				}
	 				event.data = roles;
	 			} else if (TextTransfer.getInstance().isSupportedType(event.dataType)) {
	 				TableItem selection = table.getSelection()[0];
	 				Role role = (Role) selection.getData();
	 				event.data = role.getName();
	 			}
	 		}
	 		public void dragFinished(DragSourceEvent event) {
	 			// Roles does not handle MOVE option so we leave this empty.
			}
		});
	}
	
	private void addDropTarget() {
		final Table table = tableViewer.getTable();
		
		DropTarget target = new DropTarget(table, DND.DROP_COPY | DND.DROP_MOVE | DND.DROP_DEFAULT);
		Transfer[] types = new Transfer[] { RolesTransfer.getInstance(), 
				RolesInstancesTransfer.getInstance(), 
				TextTransfer.getInstance() };
		target.setTransfer(types);
		target.addDropListener(new DropTargetAdapter() {
			public void dragEnter(DropTargetEvent event) {
				if (event.detail == DND.DROP_DEFAULT) {
					event.detail = (event.operations & DND.DROP_MOVE) != 0 ? DND.DROP_MOVE
							: (event.operations & DND.DROP_COPY) != 0 ? DND.DROP_COPY : DND.DROP_NONE;
				}
				
				for (int i = 0, n = event.dataTypes.length; i < n; i++) {
					if (event.currentDataType == null) {
						if (TextTransfer.getInstance().isSupportedType(event.dataTypes[i])) {
							event.currentDataType = event.dataTypes[i];
						} else if (TextTransfer.getInstance().isSupportedType(event.dataTypes[i])) {
							event.currentDataType = event.dataTypes[i];
						}
					}
					if (RolesTransfer.getInstance().isSupportedType(event.dataTypes[i])) {
						event.currentDataType = event.dataTypes[i];
					}
				}
			}

			public void dragOver(DropTargetEvent event) {
				event.feedback = DND.FEEDBACK_SCROLL;
			}

			public void drop(DropTargetEvent event) {
				
				boolean force = (event.detail & DND.DROP_COPY) != 0;
				if (RolesTransfer.getInstance().isSupportedType(
						event.currentDataType)) {
					Object[] roles = (Object[]) event.data;
					if (roles == null) return;
					for (int i = 0; i < roles.length; i++) {
						addRole(((Role)roles[i]).getName(), force);
					}
				} else if (TextTransfer.getInstance().isSupportedType(
						event.currentDataType)) {
					String role = (String) event.data;
					if (role == null) return;
					addRole((String)role, force);
				} else if (RolesInstancesTransfer.getInstance().isSupportedType(
						event.currentDataType)) {
					Object[] roles = (Object[]) event.data;
					if (roles == null) return;
					for (int i = 0; i < roles.length; i++) {
						addRole((((RoleInstance) roles[i]).getRole().getName()), force);
					}
				}
			}
		});
	}
	
	public void addRole(String name, boolean force) {
		while (roles.containsKey(name)) {
			if (!force) return;
			name = UITexts.get(MessageCode.ROLES_EDITOR_COPY) + name;
		}

		Role role = new Role(name);
		
		AddMemberCommand command = new AddMemberCommand(getRoles(), role);
		getCommandStack().execute(command);
	}
	
	public void setLayoutData(Object layoutData) {
		tableViewer.getTable().setLayoutData(layoutData);
	}

}
